/**
 * WordFilter.java
 * All rights reserved, Copyright (C) 2010 XPEC
 */
package training.xpec;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.math.*;

/**
 * 脏话过滤器，能够将文件中列出的脏话过滤掉。<br>
 * 注意：<br>
 * 1. 存储脏话的文件需使用utf-8编码<br>
 * 2. 由于文件格式的原因，请将脏话文件的第一行留成空白<br>
 * 3. 每行一个敏感词 
 * @author Xiaowenliang XPEC
 * 2010-4-6
 */
public class WordFilter {
	/**
	 * 替换字符，用于替代原来脏话的字符,默认为"*"
	 */
	private String replacement;
	
	/**
	 * 存储文件中的词
	 */
	private Map<Character,List<String>> wordList;
	private Map<Character,Set<String>> wordList2;
	private Map<Character,List<Integer>> wordRange;
	
	public WordFilter(Map<Character, List<String>> words, String repl){
		this.replacement = repl;
		this.wordList = words;
	}
	public WordFilter(File f, String repl){
		this.initWordDictFromFile(f);
		this.replacement = repl;
	}
	
	public WordFilter(File f){
		this(f,"*");
	}
	
	public WordFilter(Map<Character, List<String>> words){
		this(words,"*");
	}
	
	/**
	 * 用存储于文件中的脏话初始化脏话列表
	 * @param f  存储脏话的文件
	 */
	private void initWordDictFromFile(File f){
		Map<Character,List<String>> tmp = new HashMap<Character, List<String>>();
		this.wordList2 = new HashMap<Character,Set<String>>();
		this.wordRange = new HashMap<Character, List<Integer>>();
		try {
			InputStream is = new FileInputStream(f);
			BufferedReader reader = new BufferedReader(new InputStreamReader(is,"UTF-8"));
			try {
				String line = reader.readLine();
				//文件未读完
				while(line!=null){
					//如果关键词未被存入Map中
					if(line=="\n"||line.matches("\\s*")){
						line = reader.readLine();
						
						continue;
					}
					if(!tmp.containsKey(line.charAt(0))){
						//创建存储敏感词的列表
						List<String> tmplist = new ArrayList<String>();
						//添加此敏感词
						tmplist.add(line);
						//保存至Map中
						tmp.put(line.charAt(0), tmplist);
					}
					else{
						tmp.get(line.charAt(0)).add(line);
					}
					if(!wordList2.containsKey(line.charAt(0))){
						Set<String> tmpset = new HashSet<String>();
						tmpset.add(line);
						List<Integer> tmplist = new ArrayList<Integer>();
						tmplist.add(line.length());
						tmplist.add(line.length());
						wordList2.put(line.charAt(0), tmpset);
						wordRange.put(line.charAt(0), tmplist);
					}
					else{
						this.wordList2.get(line.charAt(0)).add(line);
						List<Integer> t = this.wordRange.get(line.charAt(0));
						t.set(1, Math.max(t.get(1), line.length()));
						t.set(0, Math.min(t.get(0), line.length()));
					}
					line = reader.readLine();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		catch(UnsupportedEncodingException e){
			e.printStackTrace();
		}
		this.wordList = tmp;
	}
	
	/**
	 * 过滤一句话，将其中的敏感词过滤掉
	 * @param sentence 被过滤的话
	 * @return 过滤完成后的结果
	 */
	public String filtrate(String sentence){
		StringBuffer sb = new StringBuffer();
		char[] chararray = sentence.toCharArray();
		boolean matched = false;
		//逐个扫描语句中的字符
		for(int i = 0; i < chararray.length;){
//			System.out.println("Now get character  :"+chararray[i]);
			//如果以该字符为起始字符的脏话存在
			if(wordList.containsKey(chararray[i])){
				//遍历所有以该词起始的脏话
				for(int k = 0; k < wordList.get(chararray[i]).size();k++){
					String s = wordList.get(chararray[i]).get(k);
					//遍历脏话中的每一个字符
					for(int j = 0; j < s.length(); j++){
//						System.out.println(chararray[i+j]+"is compared with "+s.charAt(j)+" of "+s);
						//逐个匹配，如果不相等
						if((j+i)>=chararray.length||chararray[j+i]!=s.charAt(j)){
							//置匹配位 失败
							matched = false;
							//跳出对这个脏话的匹配循环
							break;
						}
						//判断脏话是否被完全匹配
						if(j==s.length()-1){
							//置匹配成功
							matched = true;
							//向结果串中添加指定数量的*
							for(int t = 0; t <= j; t++){
								sb.append(replacement);
							}
							//从第i+j+1个位置开始对下一个字符的脏话匹配
							i+=j+1;
						}
					}
					//遍历完成以某一字符起始的所有脏话列表，或者因匹配成功而跳出循环后，如果匹配成功
					if(matched){
//						System.out.println(" matched.");
						//清空匹配位，为下一次匹配做准备
						matched = false;
						//终止对列表中其它脏话的匹配循环
						break;
					}
					//当且仅当匹配失败并且对以某一字符起始的所有脏话遍历结束后，才能确定匹配失败
					else if(k==wordList.get(chararray[i]).size()-1){
//						System.out.println("Iterate all patterns of "+wordList.get(chararray[i])+", match nothing.");
						matched = false;
						//将第i个字符加入到结果串中
						sb.append(chararray[i]);
						//从下一位置开始继续匹配
						i+=1;
						//终止对该字符的匹配，如果不是用break，会引起wordList.get(chararray[i]).size()的nullpoint异常。
						break;
					}
				}
			}
			//没有以该字符起始的脏话
			else{
				//将该字符加入到结果串中
				sb.append(chararray[i]);
				//从下一位置继续匹配
				i+=1;
			}
		}
		//返回结果
		return sb.toString();
	}
	/**
	 * 用于处理空格及tab的私有方法，还没有实现。
	 * @param chars 字符数组
	 * @param start 匹配开始的位置
	 * @return  距离开始匹配位置最近的非空白字符的位置
	 */
	private static int ignoreWhiteSpace(char[] chars, int start){
		for(int i = start; i < chars.length; i++)
		{
			if(chars[i]==' '||chars[i]=='	'){
				System.out.println(new String(chars)+" has white space at "+i);
				start++;
			}
			else{
				break;
			}
		}
		return start-1;
	}
	/**
	 * 过滤一句话，将其中的敏感词过滤掉
	 * @param sentence 被过滤的话
	 * @return 过滤完成后的结果
	 */
	public String filtrate2(String sentence){
		//stringbuffer 构造结果
		StringBuffer sb = new StringBuffer();
		//逐个扫描待过滤语句中的字符
		for(int i = 0; i < sentence.length();){
			//如果这个字符在敏感词字典索引中
			if(wordList2.containsKey(sentence.charAt(i))){
				/*wordRange是Map<Character,List>结构，在List索引0位置放置以该字符开头的敏感词的最短字符长度
				 * 位置1放置最大长度
				 * */
				int max = wordRange.get(sentence.charAt(i)).get(1);
				int min = wordRange.get(sentence.charAt(i)).get(0);
				//匹配标志位 初始为失败
				boolean matched = false;
				//最常匹配，从最长的敏感词开始，递减匹配 
				for(int j = max; j>=min ; j--){
					//如果当前遍历的字符的位置，加上最常敏感词的长度，大于待过滤语句的长度
					if(i+j>sentence.length()){
						//跳过本次循环，继续执行
						continue;
					}
					//如果敏感词列表(基于Set的 wordList)包含从当前遍历字符开始，长度为j的子串
					if(wordList.get(sentence.charAt(i)).contains(sentence.substring(i, i+j))){
						//将j个字符替换为*
						for(int k = 0; k < j; k++){
							sb.append("*");
						}
						//本次匹配成功，下一次从i+j位置开始遍历带过滤语句字符
						i+=j;
						matched = true;
						//停止匹配
						break;
					}
				}
				//如果匹配失败
				if(!matched){
					//将当前字符加入buffer中
					sb.append(sentence.charAt(i));
					//从下一位置继续遍历待过滤语句
					i+=1;
				}
			}
			//如果该字符不在敏感词列表索引中
			else{
				//将当前字符加入buffer中
				sb.append(sentence.charAt(i));
				//从下一位置继续遍历待过滤语句
				i+=1;
			}
		}
		return sb.toString();
	}
}
